/* $Id: Model.java 63 2010-02-14 13:52:34Z aaron.day $ */
package org.crazydays.gameplan.model;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import org.crazydays.gameplan.db.Database;
import org.crazydays.gameplan.db.io.DatabaseInputStream;
import org.crazydays.gameplan.db.io.DatabaseOutputStream;
import org.crazydays.gameplan.map.GameMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Model
 */
public class Model
{
    /** logger */
    protected final static Logger LOGGER = LoggerFactory.getLogger(Model.class);

    /** database observers */
    protected Set<IDatabaseObserver> databaseObservers =
        new HashSet<IDatabaseObserver>();

    /** game map observers */
    protected Set<IGameMapObserver> gameMapObservers =
        new HashSet<IGameMapObserver>();

    /** database */
    protected Database database;

    /** save file */
    protected File saveFile;

    /**
     * Create database.
     */
    public void createDatabase()
    {
        database = new Database();

        // notify observers
        for (IDatabaseObserver observer : databaseObservers) {
            observer.opened(database);
        }
    }

    /**
     * Open database.
     */
    public void openDatabase(File file)
        throws IOException
    {
        if (file == null) {
            throw new IllegalArgumentException("file == null");
        }

        DatabaseInputStream stream =
            new DatabaseInputStream(new FileInputStream(file));

        try {
            database = stream.readDatabase();
        } catch (IOException e) {
            LOGGER.error("Failure to open {0}: {1}", file.getPath(), e);
            throw e;
        } finally {
            stream.close();
        }

        // notify observers
        for (IDatabaseObserver observer : databaseObservers) {
            observer.opened(database);
        }
    }

    /**
     * Close database.
     */
    public void closeDatabase()
    {
        // TODO: close database

        // notify observers
        for (IDatabaseObserver observer : databaseObservers) {
            observer.closed(database);
        }

        database = null;
    }

    /**
     * Add game map.
     * 
     * @param gameMap GameMap
     */
    public void addGameMap(GameMap gameMap)
    {
        if (database == null) {
            throw new IllegalStateException("database == null");
        }

        database.addGameMap(gameMap);

        // notify observers
        for (IGameMapObserver observer : gameMapObservers) {
            observer.added(gameMap);
        }
    }

    /**
     * Add database observer.
     * 
     * @param observer Observer
     */
    public void addDatabaseObserver(IDatabaseObserver observer)
    {
        databaseObservers.add(observer);
    }

    /**
     * Remove database observer.
     * 
     * @param observer Observer
     */
    public void removeDatabaseObserver(IDatabaseObserver observer)
    {
        databaseObservers.remove(observer);
    }

    /**
     * Add game map observer.
     * 
     * @param observer Observer
     */
    public void addGameMapObserver(IGameMapObserver observer)
    {
        gameMapObservers.add(observer);
    }

    /**
     * Remove game map observer.
     * 
     * @param observer Observer
     */
    public void removeGameMapObserver(IGameMapObserver observer)
    {
        gameMapObservers.remove(observer);
    }

    /**
     * Get game map by name.
     * 
     * @param name Name
     * @return GameMap
     */
    public GameMap getGameMap(String name)
    {
        if (database == null) {
            throw new IllegalStateException("database == null");
        }
        if (name == null) {
            throw new IllegalArgumentException("name == null");
        }

        for (GameMap gameMap : database.getGameMaps()) {
            if (name.equals(gameMap.getName())) {
                return gameMap;
            }
        }

        LOGGER.error("Unable to locate gameMap: {}", name);
        return null;
    }

    /**
     * Set save file.
     * 
     * @param file File
     */
    public void setSaveFile(File file)
    {
        this.saveFile = file;
    }

    /**
     * Is save file set.
     * 
     * @return Set
     */
    public boolean isSaveFileSet()
    {
        return saveFile != null;
    }

    /**
     * Save database.
     * 
     * @throws IOException
     */
    public void save()
        throws IOException
    {
        if (database == null) {
            throw new IllegalStateException("database == null");
        }
        if (saveFile == null) {
            throw new IllegalStateException("saveFile == null");
        }

        DatabaseOutputStream stream =
            new DatabaseOutputStream(new FileOutputStream(saveFile));
        try {
            stream.write(database);
        } finally {
            stream.close();
        }
    }
}
